#include "stdafx.h"

partyMember_s partyMember[18] = { NULL };
FreecamValue Freecam = { vec3(), vec3(), vec3(), vec3(), false };

typedef void(__cdecl *Cbuf_AddTextBO2_t)(int localClientNum, const char *text);
Cbuf_AddTextBO2_t Cbuf_AddText = (Cbuf_AddTextBO2_t)0x824015E0;

void(*R_AddCmdDrawText)(char* text, int maxChars, int font, float x, float y, float xScale, float yScale, float rotation, color& color, int style)
= (void(*)(char*, int, int, float, float, float, float, float, color&, int))0x828B8BA0;

void(*R_AddCmdDrawStretchPic)(float x, float y, float w, float h, float s0, float t0, float s1, float t1, color& color, int material)
= (void(*)(float x, float y, float width, float height, float xScale, float yScale, float xay, float yay, color & color, int material))0x828B86C0;

int(*R_TextWidth)(int localClientNum, char* text, int maxChars, int font)
= (int(*)(int, char*, int, int))0x828B6FD8;

int(*R_TextHeight)(int font)
= (int(*)(int))0x82490390;

void(*CG_DrawRotatedPicPhysical)(int ScreenPlacement, float x, float y, float w, float h, float angle, color& color, int material)
= (void(*)(int, float, float, float, float, float, color&, int))0x821C7F58;

int(*Material_RegisterHandle)(char* material, int imageTrack)
= (int(*)(char*, int))0x828B78F0;

int(*CL_RegisterFont)(char* font, int imageTrack)
= (int(*)(char*, int))0x82275F78;

void(*CL_DrawTextPhysicalWithEffects)(char* text, int maxChars, int font, float x, float y, float w, float xScale, float yScale, color& colors, int style, color& glowColor, int fxMaterial, int fxMaterialGlow, int fxBirthTime, int fxLetterTime, int fxDecayStartTime, int fxDecayDuration)
= (void(*)(char*, int, int, float, float, float, float, float, color &, int, color&, int, int, int, int, int, int))0x82275830;

char* (*va)(char* fmt, ...)
= (char* (*)(char*, ...))0x8249E440;

void(*MSG_Init)(msg_t* msg, char* data, int length)
 = (void(*)(msg_t*, char*, int))0x824191F8;

void(*MSG_WriteLong)(msg_t* msg, int value)
 = (void(*)(msg_t*, int))0x8241A440;

void(*MSG_WriteByte)(msg_t* msg, int value)
 = (void(*)(msg_t*, int))0x82416D98;

void(*MSG_WriteShort)(msg_t* msg, int value)
= (void(*)(msg_t*, int))0x8241A3D8;

void(*MSG_WriteData)(msg_t* msg, char* data, int length)
= (void(*)(msg_t*, char*, int))0x82416DD0;

void(*NET_SendPacket)(netsrc_t socket, int length, char* data, netAdr_t to)
= (void(*)(netsrc_t, int, char*, netAdr_t))0x8241D3C0;

int(*Dvar_FindVar)(const char* dvarName)
= (int(*)(const char*))0x82496430;

bool(*CL_AllLocalClientsDisconnected)(void)
= (bool(*)(void))0x827504D0;

bool(*CG_IsEntityFriendlyNotEnemy)(int localClientNum, centity_s* entity)
= (bool(*)(int, centity_s*))0x821CD948;

short(*SL_GetString)(const char* bone, int localClientNum)
= (short(*)(const char*, int))0x82533528;

int(*Com_GetClientDObj)(int localClientNum, int unk)
= (int(*)(int, int))0x82414578;

bool(*CG_DObjGetWorldTagPos)(centity_s* cent, int DObj, short tagName, vec3& pos)
= (bool(*)(centity_s*, int, short, vec3&))0x821D03F0;

int(*BG_GetWeaponDef)(int weaponNum)
= (int(*)(int))0x826BF988;

bool(*BG_UsingVehicleWeapon)(playerState_s* ps)
= (bool(*)(playerState_s*))0x826B5048;

void(*BG_GetSpreadForWeapon)(playerState_s* ps, int weaponId, float* x, float* y)
= (void(*)(playerState_s*, int, float*, float*))0x826BB4E0;

int(*TransformSeed)(int* seed)
= (int(*)(int*))0x826961B8;

float(*RandomBulletDir)(int* randSeed)
= (float(*)(int*))0x82696250;

void(*sub_82697FC0)(int* randSeed)
= (void(*)(int*))0x82697FC0;

void(*FireBulletPenetrate)(int localClientNum, BulletFireParams* bp, int weaponId, centity_s* attacker, vec3& Origin, int one, bool unk, BulletTraceResults* br)
= (void(*)(int, BulletFireParams*, int, centity_s*, vec3&, int, bool, BulletTraceResults*))0x82258840;

void(*CG_GetPlayerViewOrigin)(int localClientNum, playerState_s* ps, vec3& origin)
= (void(*)(int, playerState_s*, vec3&))0x822544B0;

void(*CG_LocationalTrace)(trace_t* trace, vec3* start, vec3* end, int passEntityNum, int contMask, int u1, int u2)
= (void(*)(trace_t*, vec3*, vec3*, int, int, int, int))0x8225C568;

int(*CG_GetWeaponIndexForName)(const char* weaponName)
= (int(*)(const char*))0x826C06E8;

void(*AngleVectors)(vec3* angles, vec3* forward, vec3* right, vec3* up)
= (void(*)(vec3*, vec3*, vec3*, vec3*))0x8248E408;

void(*AnglesToAxis)(vec3* angles, vec3* axis)
= (void(*)(vec3*, vec3*))0x8248E540;

DWORD gClient(int clientNum) {
	return (0x83551A10 + (clientNum * 0x57F8));
}

DWORD gEntity(int clientNum) {
	return (0x833D0640 + (clientNum * 0x31C));
}

uiContext_s get_uiContext() {
	return *(uiContext_s*)(0x83BA29F0);
}

centity_s get_centity(int clientNum) {
	centity_s* entity = (centity_s*)*(int*)(0x82BBC554);
	return entity[clientNum];
}

cg_s* get_cg() {
	return (cg_s*)*(int*)(0x82BBAE68);
}

clientActive_s* get_clientActive() {
	return (clientActive_s*)*(int*)(0x82C70F4C);
}

partyData_s* get_active_party()
{
	partyData_s* partyData = (partyData_s*)(0x82CA0898);
	partyData_s* lobbyData = (partyData_s*)(0x82CAC160);

	return (partyData->party_systemActive == 1 && partyData->inParty) ? partyData : lobbyData;
}

bool IsSpectating() {
	if (get_cg()->playerState.otherFlags & 0x1A)
		return true;

	return false;
}

//Render

void DrawText(char* text, float x, float y, int font, float fontScale, color color) { //gradient_fadein
	R_AddCmdDrawText(text, 0x7FFFFFFF, font, x, y, fontScale, fontScale, 0, color, 0);
}

void DrawTextWithEffects(char* text, float x, float y, int font, float fontScale, color textColor, color glowColor) {
	CL_DrawTextPhysicalWithEffects(text, 0x7FFFFFFF, font, x, y, 1.0f, fontScale, fontScale, textColor, 0, glowColor, 0, 0, 0, 0, 0, 0);
}

void DrawShader(float x, float y, float width, float height, int material, float angle, color color) {
	CG_DrawRotatedPicPhysical(0x82CBC168, x, y, width, height, angle, color, material);
}

void DrawShaderWithOutline(float x, float y, float w, float h, color backgroundColor, color colorLeft, color colorRight, float lineWidth) {
	//DrawShader(x, y, lineWidth, h, menuDef::gradientMaterial, 180, colorRight); //left
	DrawShader(x, y, lineWidth, h, menuDef::gradientMaterial, 0, colorLeft);

	DrawShader(x + lineWidth, y, w - lineWidth, lineWidth, menuDef::gradientMaterial, 180, colorLeft);//top
	DrawShader(x + lineWidth, y, w - lineWidth, lineWidth, menuDef::gradientMaterial, 0, colorRight);

	DrawShader(x + lineWidth, y + h - lineWidth, w - (lineWidth * 2) + 2, lineWidth, menuDef::gradientMaterial, 180, colorLeft); //down
	DrawShader(x + lineWidth, y + h - lineWidth, w - (lineWidth * 2) + 2, lineWidth, menuDef::gradientMaterial, 0, colorRight);

	//DrawShader(x + w - lineWidth, y + lineWidth, lineWidth, h - lineWidth, menuDef::gradientMaterial, 180, colorRight); //right
	DrawShader(x + w - lineWidth, y + lineWidth, lineWidth, h - lineWidth, menuDef::gradientMaterial, 0, colorRight);

	DrawShader(x + lineWidth, y + lineWidth, w - (lineWidth * 2), h - (lineWidth * 2), menuDef::whiteMaterial, 0, backgroundColor);
}


void DrawCenteredText(char* text, float x, float y, float fontScale, color color, float tabWidth) {
	FLOAT textWidth = ((R_TextWidth(0, text, 0x7FFFFFFF, menuDef::normalFont) / 2) * fontScale);
	FLOAT tabX = (tabWidth / 2);

	DrawText(text, (x + tabX) - textWidth, y, menuDef::normalFont, fontScale, color);
}

void DrawInfobox(char* text, float x, float y, int Index) {
	float textWidth = (R_TextWidth(0, text, 0x7FFFFFFF, menuDef::normalFont) * 0.7f);

	DrawShaderWithOutline((x - textWidth) + 5, (y + (Index * 24)), (textWidth + 10.0f), 22.0f, colorBackground, colorAccentPrimary, secondaryColor, 2);

	DrawText(text, ((x - textWidth) + 10), (y + (Index * 24.0f) + 22), menuDef::normalFont, 0.7f, colorWhite);
}

void DrawInstruction(char* text, float x, float y, int font, float fontSize, color color, int index) {
	char pInstructionText[250] = { 0 };
	
	sprintf(pInstructionText, "%s", text);

	float fInstructionWidth = (R_TextWidth(0, pInstructionText, 0x7FFFFFFF, font) * fontSize);

	DrawShaderWithOutline(x, (y - (index * 24.0f)), (fInstructionWidth + 10.0f), 22, colorBackground, colorAccentPrimary, secondaryColor, 2);
	DrawText(pInstructionText, (x + 5.0f), ((y + 21.0f) - (index * 24.0f)), font, fontSize, colorWhite);
}

void DrawLine(float X1, float Y1, float X2, float Y2, float width, color color) {
	float X, Y, Angle, L1, L2, H1;
	H1 = Y2 - Y1;
	L1 = X2 - X1;
	L2 = sqrt(L1 * L1 + H1 * H1);
	X = X1 + ((L1 - L2) / 2);
	Y = Y1 + (H1 / 2);
	Angle = (float)atan(H1 / L1) * (180.f / 3.14159265358979323846f);
	CG_DrawRotatedPicPhysical(0x82CBC168, X, Y, L2, width, Angle, color, menuDef::whiteMaterial);
}

void DrawBox(FLOAT X, FLOAT Y, FLOAT W, FLOAT H, color Color) {
	R_AddCmdDrawStretchPic(X, Y, W, H, 4, 4, 1, 1, Color, menuDef::whiteMaterial);
}

float CalculateXValueWithAlign(float x, char* text, DWORD font, float scale, eJustify justify) {
	if (justify == JustifyLeft)
		return x;
	if (justify == JustifyCenter)
		return x - ((R_TextWidth(0, text, 0x7FFFFFFF, font) / 2) * scale);
	if (justify == JustifyRight)
		return x - (R_TextWidth(0, text, 0x7FFFFFFF, font) * scale);

	return 0;
}


void DrawString(char* text, float x, float y, DWORD font, float scale, color color, eJustify justify) {
	float defaultX = CalculateXValueWithAlign(x, text, font, scale, justify);

	DrawText(text, defaultX, y, font, scale, color);
}

bool Dvar_GetBool(char* dvarName) {
	int dvar_t = Dvar_FindVar(dvarName);
	if (dvar_t == 0)
		return false;
	return *(bool*)(dvar_t + 0x18);
}

bool CheckIsMigrating() {
	return *(int*)(0x82C6FDC8 + 0x10) != 0;
}



bool CheckInGame() {
	// Make sure we are connected to a game session
	CG::bConnected = !CL_AllLocalClientsDisconnected() ? TRUE : FALSE;

	// Check the cl_ingame dvar
	bool bInGame = Dvar_GetBool("cl_ingame");
	if (!bInGame) {
		if (CG::bQuittingGame) {
			CG::bQuittingGame = FALSE;
		}
	}

	// If we're in game and all local clients aren't disconnected, we're fully ingame.
	return bInGame && CG::bConnected && !CheckIsMigrating() && !CG::bQuittingGame;
}

bool IsEnemy(int i) {
	return (!CG_IsEntityFriendlyNotEnemy(0, &get_centity(i)));
}

bool IsAlive(int i) {
	if ((*(int*)((DWORD)&get_centity(i) + 0x36C) & 0x40000000) == 0 || (*(int*)((DWORD)&get_centity(i) + 0x1D4) & 0x40000))
		return false;

	return true;
}

bool WorldToScreen(vec3& WorldLocation, vec2& Screen) {
	vec3 Local = WorldLocation - get_cg()->refDef.viewOrigin, Transform;
	Transform.x = Local.dot(get_cg()->refDef.viewAxis[1]);
	Transform.y = Local.dot(get_cg()->refDef.viewAxis[2]);
	Transform.z = Local.dot(get_cg()->refDef.viewAxis[0]);

	if (Transform.z < 0.01)
	{
		return false;
	}

	Screen.x = (get_cg()->refDef.Width / 2) * (1 - (Transform.x / get_cg()->refDef.tanHalfFov.x / Transform.z));
	Screen.y = (get_cg()->refDef.Height / 2) * (1 - (Transform.y / get_cg()->refDef.tanHalfFov.y / Transform.z));

	return (Transform.z > 0);
}

vec3 GetTagPos(centity_s* cent, short tag) {
	vec3 tagPos = vec3();
	int dobj = Com_GetClientDObj(cent->ClientNumber, 0);

	if (dobj == 0)
		return cent->Origin;

	if (CG_DObjGetWorldTagPos(cent, dobj, tag, tagPos)) {
		return tagPos;
	}

	return vec3();
}

void getWeaponAspectRatio(DWORD killIconType, float* aspectWidth, float* aspectHeight) {
	float width = 1.4f, height = 1.4f;
	if (killIconType == 0) //1:1
	{
		width = 1.4f;
		height = 1.4f;
	}
	else if (killIconType == 1) //2:1
	{
		width = 2.8f;
		height = 1.4f;
	}
	else if (killIconType == 2) //3:1
	{
		width = 2.8f;
		height = 0.7f;
	}
	width *= 20.0f;
	height *= 20.0f;
	*aspectWidth = width;
	*aspectHeight = height;
}

int GetWeaponIcon(int Weapon, float* aspectX, float* aspectY) {
	int weaponDef = BG_GetWeaponDef(Weapon);
	if (weaponDef) {
		DWORD weapDef2 = ((DWORD(*)(...))0x826BF970)(Weapon);
		if (weapDef2) {
			DWORD killIconType = *(DWORD*)(*(DWORD*)(weapDef2 + 0x8) + 0x664);

			getWeaponAspectRatio(killIconType, aspectX, aspectY);
			return *(int*)(weaponDef + 0x3AC);
		}
	}
	return 0;
}

int GetLocalClientMaxHealth() {
	cg_s* cg = get_cg();

	if (cg) {
		return *(int*)((int)cg + 0x2D0);
	}
	return 0;
}

int GetLocalClientHealth() {
	cg_s* cg = get_cg();

	if (cg) {
		return *(int*)((int)cg + 0x2C8);
	}
	return 0;
}

vec3 GetPlayerViewOrigin() {
	vec3 viewOrigin = vec3();
	CG_GetPlayerViewOrigin(0, &get_cg()->playerState, viewOrigin);
	return viewOrigin;
}

bool IsClientVisible(int clientNum, short tag) {
	trace_t trace;

	vec3 tagPos = GetTagPos(&get_centity(clientNum), tag);
	vec3 viewOrigin = GetPlayerViewOrigin();

	if (!tagPos.IsNull() && !viewOrigin.IsNull()) {
		CG_LocationalTrace(&trace, &viewOrigin, &tagPos, clientNum, 0x280E833, FALSE, FALSE);
	}
	else {
		return false;
	}

	// Check if the trace hit a shield
	if (trace.partGroup == 0x14) {
		return false;
	}

	return (trace.fFraction >= 0.95f);
}

FLOAT ConvertThumbstickValue(SHORT sThumbstickValue, SHORT sDeadZone) {
	if (sThumbstickValue > +sDeadZone)
		return (sThumbstickValue - sDeadZone) / (32767.0f - sDeadZone);

	if (sThumbstickValue < -sDeadZone)
		return (sThumbstickValue + sDeadZone + 1.0f) / (32767.0f - sDeadZone);

	return 0.0f;
}

vec3 ClampAngles(vec3 angles) {
	if (angles.x > 69.0f)
		angles.x = 69.0f;

	if (angles.x < -69.0f)
		angles.x = -69.0f;
	//////////////////////////////
	if (angles.y > 180.0f)
		angles.y -= 360.0f;

	if (angles.y < -180.0f)
		angles.y += 360.0f;

	return angles;
}


void CG_CalcViewValues() {
	static bool valueSaved = false;
	if (MenuOptions::bRME_NoClipBind)
	{
		bool bIsDown = menuDef::state.Gamepad.bRightTrigger >= 100;

		if (bIsDown && !valueSaved)
		{
			Freecam.currentOrigin = get_cg()->refDef.viewOrigin;
			Freecam.currentAngles = *(vec3*)((int)get_cg() + 0x64920); //refdefViewAngles
			Freecam.savedAngles = vec3(*(float*)((int)get_cg() + 0x69810), *(float*)((int)get_cg() + 0x69814), 0.0f);
			Freecam.Noclip_RME = true;

			valueSaved = true;
		}
		else if (!bIsDown && valueSaved)
		{
			//cg->refdefViewAngles = Freecam.currentAngles;
			*(vec3*)((int)get_cg() + 0x64920) = Freecam.currentAngles;
			Freecam.Noclip_RME = false;
			valueSaved = false;

			g_Netchan.setup_buffer_rme();
			g_Netchan.write_byte(gClient(get_cg()->clientNumber) + 0x5684, 0x00); // unlock movement flag
		}
	}

	if (Freecam.Noclip_RME)
	{
		bool bIsDown = menuDef::state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB;

		vec3 forward, right;
		AngleVectors(&Freecam.currentAngles, &forward, &right, 0);

		float movementSpeed = bIsDown ? 15.0 : 7.5;
		float cameraSpeed = 3.0;

		float forwardMove = ConvertThumbstickValue(menuDef::state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) * movementSpeed;
		float rightMove = ConvertThumbstickValue(menuDef::state.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) * movementSpeed;

		float pitchMove = -ConvertThumbstickValue(menuDef::state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) * cameraSpeed;
		float yawMove = -ConvertThumbstickValue(menuDef::state.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) * cameraSpeed;

		Freecam.currentOrigin.x += forward.x * forwardMove + right.x * rightMove;
		Freecam.currentOrigin.y += forward.y * forwardMove + right.y * rightMove;
		Freecam.currentOrigin.z += forward.z * forwardMove + right.z * rightMove;

		Freecam.currentAngles.x += pitchMove;
		Freecam.currentAngles.y += yawMove;

		Freecam.currentAngles = ClampAngles(Freecam.currentAngles);

		get_cg()->refDef.viewOrigin = Freecam.currentOrigin;
		*(vec3*)((int)get_cg() + 0x64920) = Freecam.currentAngles;
		AnglesToAxis(&Freecam.currentAngles, get_cg()->refDef.viewAxis);

		if (Freecam.Noclip_RME)
		{
			vec3 newOrigin[2];
			newOrigin[0] = vec3(get_cg()->refDef.viewOrigin.x, get_cg()->refDef.viewOrigin.y, get_cg()->refDef.viewOrigin.z - get_cg()->playerState.viewHeightCurrent);
			newOrigin[1] = vec3(0.0f, 0.0f, 0.0f);

			g_Netchan.setup_buffer_rme();
			g_Netchan.write(gClient(get_cg()->clientNumber) + 0x28, (char*)&newOrigin, sizeof(vec3) * 2); // write origin + velocity
		}
	}
}

void NoClipToggle() {
	if (CheckInGame() && CG::bLobbyInitialized) {
		if (MenuOptions::bRME_NoClip) {

			g_Netchan.setup_buffer_rme();

			Freecam.currentOrigin = get_cg()->refDef.viewOrigin;
			Freecam.currentAngles = *(vec3*)((int)get_cg() + 0x64920);
			Freecam.savedAngles = vec3(*(float*)((int)get_cg() + 0x69810), *(float*)((int)get_cg() + 0x69814), 0.0f);
			Freecam.Noclip_RME = true;
		}
		else {
			*(vec3*)((int)get_cg() + 0x64920) = Freecam.currentAngles;

			Freecam.Noclip_RME = false;

			g_Netchan.write_byte(gClient(get_cg()->clientNumber) + 0x5684, 0x00); // unlock movement flag
		}
	}
}

